/*******************************************************************************
 *  Copyright (c) 2002, 2012 IBM Corporation and others.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  which accompanies this distribution, and is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  Contributors:
 *  IBM Rational Software - Initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.managedbuilder.internal.core;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.ConsoleOutputStream;
import org.eclipse.cdt.core.ErrorParserManager;
import org.eclipse.cdt.core.ICommandLauncher;
import org.eclipse.cdt.core.IMarkerGenerator;
import org.eclipse.cdt.core.model.ICModelMarker;
import org.eclipse.cdt.core.resources.ACBuilder;
import org.eclipse.cdt.core.resources.IConsole;
import org.eclipse.cdt.core.resources.RefreshScopeManager;
import org.eclipse.cdt.internal.core.BuildRunnerHelper;
import org.eclipse.cdt.managedbuilder.buildmodel.BuildDescriptionManager;
import org.eclipse.cdt.managedbuilder.buildmodel.IBuildDescription;
import org.eclipse.cdt.managedbuilder.buildmodel.IBuildIOType;
import org.eclipse.cdt.managedbuilder.buildmodel.IBuildResource;
import org.eclipse.cdt.managedbuilder.buildmodel.IBuildStep;
import org.eclipse.cdt.managedbuilder.core.IBuilder;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin;
import org.eclipse.cdt.managedbuilder.envvar.IBuildEnvironmentVariable;
import org.eclipse.cdt.managedbuilder.internal.buildmodel.DescriptionBuilder;
import org.eclipse.cdt.managedbuilder.internal.buildmodel.IBuildModelBuilder;
import org.eclipse.cdt.managedbuilder.internal.buildmodel.ParallelBuilder;
import org.eclipse.cdt.managedbuilder.internal.buildmodel.StepBuilder;
import org.eclipse.cdt.managedbuilder.macros.BuildMacroException;
import org.eclipse.cdt.managedbuilder.macros.IBuildMacroProvider;
import org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator;
import org.eclipse.cdt.utils.EFSExtensionManager;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;

/**
 * This is the incremental builder associated with a managed build project. It dynamically
 * decides the makefile generator it wants to use for a specific target.
 *
 * @since 1.2
 */
public class GeneratedMakefileBuilder extends ACBuilder {
	/**
	 * @since 1.2
	 */
	public class ResourceDeltaVisitor implements IResourceDeltaVisitor {
		private String buildGoalName;
		private IManagedBuildInfo buildInfo;
		private boolean incrBuildNeeded = false;
		private boolean fullBuildNeeded = false;
		private List<String> reservedNames;

		public ResourceDeltaVisitor(IManagedBuildInfo info) {
			buildInfo = info;
			String ext = buildInfo.getBuildArtifactExtension();
			//try to resolve build macros in the build artifact extension
			try{
				ext = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
						ext,
						"", //$NON-NLS-1$
						" ", //$NON-NLS-1$
						IBuildMacroProvider.CONTEXT_CONFIGURATION,
						info.getDefaultConfiguration());
			} catch (BuildMacroException e){
			}

			String name = buildInfo.getBuildArtifactName();
			//try to resolve build macros in the build artifact name
			try{
				String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
						name,
						"", //$NON-NLS-1$
						" ", //$NON-NLS-1$
						IBuildMacroProvider.CONTEXT_CONFIGURATION,
						info.getDefaultConfiguration());
				if((resolved = resolved.trim()).length() > 0)
					name = resolved;
			} catch (BuildMacroException e){
			}

			if (ext.length() > 0) {
				buildGoalName = buildInfo.getOutputPrefix(ext) + name + IManagedBuilderMakefileGenerator.DOT + ext;
			} else {
				buildGoalName = name;
			}
			reservedNames = Arrays.asList(new String[]{".cdtbuild", ".cdtproject", ".project"});	//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		}

		private boolean isGeneratedResource(IResource resource) {
			// Is this a generated directory ...
			IPath path = resource.getProjectRelativePath();
			String[] configNames = buildInfo.getConfigurationNames();
			for (int i = 0; i < configNames.length; i++) {
				String name = configNames[i];
				IPath root = new Path(name);
				// It is if it is a root of the resource pathname
				if (root.isPrefixOf(path)) return true;
			}
			return false;
		}

		private boolean isProjectFile(IResource resource) {
			return reservedNames.contains(resource.getName());
		}

		public boolean shouldBuildIncr() {
			return incrBuildNeeded;
		}

		public boolean shouldBuildFull() {
			return fullBuildNeeded;
		}

		@Override
		public boolean visit(IResourceDelta delta) throws CoreException {
			IResource resource = delta.getResource();
			// If the project has changed, then a build is needed and we can stop
			if (resource != null && resource.getProject() == getProject()) {
				switch(resource.getType()){
				case IResource.FILE:
					String name = resource.getName();
					if ((!name.equals(buildGoalName) &&
						// TODO:  Also need to check for secondary outputs
						(resource.isDerived() ||
						(isProjectFile(resource)) ||
						(isGeneratedResource(resource))))) {
						 // The resource that changed has attributes which make it uninteresting,
						 // so don't do anything
					}
					else {
						//  TODO:  Should we do extra checks here to determine if a build is really needed,
						//         or do you just do exclusion checks like above?
						//         We could check for:
						//         o  The build goal name
						//         o  A secondary output
						//         o  An input file to a tool:
						//            o  Has an extension of a source file used by a tool
						//            o  Has an extension of a header file used by a tool
						//            o  Has the name of an input file specified in an InputType via:
						//               o  An Option
						//               o  An AdditionalInput
						//
						//if (resourceName.equals(buildGoalName) ||
						//	(buildInfo.buildsFileType(ext) || buildInfo.isHeaderFile(ext))) {

							// We need to do an incremental build, at least
							incrBuildNeeded = true;
							if (delta.getKind() == IResourceDelta.REMOVED) {
								// If a meaningful resource was removed, then force a full build
								// This is required because an incremental build will trigger make to
								// do nothing for a missing source, since the state after the file
								// removal is uptodate, as far as make is concerned
								// A full build will clean, and ultimately trigger a relink without
								// the object generated from the deleted source, which is what we want
								fullBuildNeeded = true;
								// There is no point in checking anything else since we have
								// decided to do a full build anyway
								break;
							}

						//}
					}

					return false;
				}
			}
			return true;
		}
	}

	private static class OtherConfigVerifier implements IResourceDeltaVisitor {
		IConfiguration config;
		IConfiguration configs[];
		Configuration otherConfigs[];
		int resourceChangeState;

		private static final IPath[] ignoreList = {
			new Path(".cdtproject"), //$NON-NLS-1$
			new Path(".cproject"), //$NON-NLS-1$
			new Path(".cdtbuild"), //$NON-NLS-1$
			new Path(".settings"), //$NON-NLS-1$
		};

		OtherConfigVerifier(IConfiguration cfg){
			config = cfg;
			configs = cfg.getManagedProject().getConfigurations();
			otherConfigs = new Configuration[configs.length - 1];
			int counter = 0;
			for(int i = 0; i < configs.length; i++){
				if(configs[i] != config)
					otherConfigs[counter++] = (Configuration)configs[i];
			}
		}

		@Override
		public boolean visit(IResourceDelta delta) throws CoreException {

			IResource rc = delta.getResource();
			if(rc.getType() == IResource.FILE){
				if(isResourceValuable(rc))
					resourceChangeState |= delta.getKind();
				return false;
			}
			return !isGeneratedForConfig(rc, config) && isResourceValuable(rc);
		}

		public void updateOtherConfigs(IResourceDelta delta){
			if(delta == null)
				resourceChangeState = ~0;
			else {
				try {
					delta.accept(this);
				} catch (CoreException e) {
					resourceChangeState = ~0;
				}
			}

			setResourceChangeStateForOtherConfigs();
		}

		private void setResourceChangeStateForOtherConfigs(){
			for(int i = 0; i < otherConfigs.length; i++){
				otherConfigs[i].addResourceChangeState(resourceChangeState);
			}
		}

		private boolean isGeneratedForConfig(IResource resource, IConfiguration cfg) {
			// Is this a generated directory ...
			IPath path = resource.getProjectRelativePath();
			IPath root = new Path(cfg.getName());
			// It is if it is a root of the resource pathname
			if (root.isPrefixOf(path))
				return true;
			return false;
		}

		private boolean isResourceValuable(IResource rc){
			IPath path = rc.getProjectRelativePath();
			for(int i = 0; i < ignoreList.length; i++){
				if(ignoreList[i].equals(path))
					return false;
			}
			return true;
		}
	}

	// String constants
	private static final String BUILD_ERROR = "ManagedMakeBuilder.message.error";	//$NON-NLS-1$
	private static final String BUILD_FINISHED = "ManagedMakeBuilder.message.finished";	//$NON-NLS-1$
	private static final String CONSOLE_HEADER = "ManagedMakeBuilder.message.console.header";	//$NON-NLS-1$
	private static final String ERROR_HEADER = "GeneratedmakefileBuilder error [";	//$NON-NLS-1$
	private static final String MAKE = "ManagedMakeBuilder.message.make";	//$NON-NLS-1$
	private static final String MARKERS = "ManagedMakeBuilder.message.creating.markers";	//$NON-NLS-1$
	private static final String NEWLINE = System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
	private static final String NOTHING_BUILT = "ManagedMakeBuilder.message.no.build";	//$NON-NLS-1$
	private static final String REFRESH = "ManagedMakeBuilder.message.updating";	//$NON-NLS-1$
	private static final String REFRESH_ERROR = BUILD_ERROR + ".refresh";	//$NON-NLS-1$
	private static final String TRACE_FOOTER = "]: ";	//$NON-NLS-1$
	private static final String TRACE_HEADER = "GeneratedmakefileBuilder trace [";	//$NON-NLS-1$
	private static final String TYPE_CLEAN = "ManagedMakeBuilder.type.clean";	//$NON-NLS-1$
	private static final String TYPE_INC = "ManagedMakeBuider.type.incremental";	//$NON-NLS-1$
	private static final String WARNING_UNSUPPORTED_CONFIGURATION = "ManagedMakeBuilder.warning.unsupported.configuration";	//$NON-NLS-1$
	private static final String BUILD_CANCELLED = "ManagedMakeBuilder.message.cancelled";	//$NON-NLS-1$
	private static final String BUILD_FINISHED_WITH_ERRS = "ManagedMakeBuilder.message.finished.with.errs";	//$NON-NLS-1$
	private static final String BUILD_FAILED_ERR = "ManagedMakeBuilder.message.internal.builder.error";	//$NON-NLS-1$
	private static final String BUILD_STOPPED_ERR = "ManagedMakeBuilder.message.stopped.error";	//$NON-NLS-1$
	private static final String INTERNAL_BUILDER_HEADER_NOTE = "ManagedMakeBuilder.message.internal.builder.header.note";	//$NON-NLS-1$
	private static final String TYPE_REBUILD = "ManagedMakeBuider.type.rebuild";	//$NON-NLS-1$
	private static final String INTERNAL_BUILDER = "ManagedMakeBuilder.message.internal.builder";	//$NON-NLS-1$

	private static final int PROGRESS_MONITOR_SCALE = 100;
	private static final int TICKS_STREAM_PROGRESS_MONITOR = 1 * PROGRESS_MONITOR_SCALE;
	private static final int TICKS_DELETE_MARKERS = 1 * PROGRESS_MONITOR_SCALE;

	public static boolean VERBOSE = false;

	// Local variables
	protected Vector<IStatus> generationProblems;
	protected IProject[] referencedProjects;
	protected List<IResource> resourcesToBuild;
	private IConsole console;
	public static void outputTrace(String resourceName, String message) {
		if (VERBOSE) {
			System.out.println(TRACE_HEADER + resourceName + TRACE_FOOTER + message + NEWLINE);
		}
	}

	public static void outputError(String resourceName, String message) {
		if (VERBOSE) {
			System.err.println(ERROR_HEADER + resourceName + TRACE_FOOTER + message + NEWLINE);
		}
	}

	/**
	 * Zero-argument constructor needed to fulfill the contract of an
	 * incremental builder.
	 */
	public GeneratedMakefileBuilder() {
	}

	private void addBuilderMarkers(ErrorParserManager epm) {
		IWorkspaceRoot root = CCorePlugin.getWorkspace().getRoot();
		Iterator<IStatus> iter = getGenerationProblems().iterator();
		while (iter.hasNext()) {
			IStatus stat = iter.next();
			IResource location = root.findMember(stat.getMessage());
			if (stat.getCode() == IManagedBuilderMakefileGenerator.SPACES_IN_PATH) {
				epm.generateMarker(location, -1, ManagedMakeMessages.getResourceString("MakefileGenerator.error.spaces"), IMarkerGenerator.SEVERITY_WARNING, null);	//$NON-NLS-1$
			}
		}
	}

	/* (non-javadoc)
	 * Emits a message to the console indicating that there were no source files to build
	 * @param buildType
	 * @param status
	 * @param configName
	 */
	private void emitNoSourceMessage(int buildType, IStatus status, String configName) throws CoreException {
		try {
			StringBuffer buf = new StringBuffer();
			IConsole console = CCorePlugin.getDefault().getConsole();
			console.start(getProject());
			ConsoleOutputStream consoleOutStream = console.getOutputStream();
			// Report a successful clean
			String[] consoleHeader = new String[3];
			if (buildType == FULL_BUILD || buildType == INCREMENTAL_BUILD) {
				consoleHeader[0] = ManagedMakeMessages.getResourceString(TYPE_INC);
			} else {
				consoleHeader[0] = new String();
				outputError(getProject().getName(), "The given build type is not supported in this context");	//$NON-NLS-1$
			}
			consoleHeader[1] = configName;
			consoleHeader[2] = getProject().getName();
			buf.append(NEWLINE);
			buf.append(ManagedMakeMessages.getFormattedString(CONSOLE_HEADER, consoleHeader)).append(NEWLINE);
			buf.append(NEWLINE);
			buf.append(status.getMessage()).append(NEWLINE);
			consoleOutStream.write(buf.toString().getBytes());
			consoleOutStream.flush();
			consoleOutStream.close();
		} catch (CoreException e) {
			// Throw the exception back to the builder
			throw e;
		} catch (IOException io) {	//  Ignore console failures...
		}
	}

	/**
	 *
	 * This method has been created so that subclasses can override how the builder obtains its
	 * build info.  The default implementation retrieves the info from the build manager.
	 *
	 * @return An IManagedBuildInfo object representing the build info.
	 */
	protected IManagedBuildInfo getBuildInfo() {
		return ManagedBuildManager.getBuildInfo(getProject());
	}

	/* (non-Javadoc)
	 * @see org.eclipse.core.internal.events.InternalBuilder#build(int, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
	 */
	@Override
	protected IProject[] build(int kind, @SuppressWarnings("rawtypes") Map argsMap, IProgressMonitor monitor) throws CoreException {
		@SuppressWarnings("unchecked")
		Map<String, String> args = argsMap;
		if (DEBUG_EVENTS)
			printEvent(kind, args);

		// We should always tell the build system what projects we reference
		referencedProjects = getProject().getReferencedProjects();

		// Get the build information
		IManagedBuildInfo info = getBuildInfo();
		if (info == null) {
			outputError(getProject().getName(), "Build information was not found");	//$NON-NLS-1$
			return referencedProjects;
		}
		if (!info.isValid()) {
			outputError(getProject().getName(), "Build information is not valid");	//$NON-NLS-1$
			return referencedProjects;
		}

		IConfiguration[] cfgs = null;
		if (needAllConfigBuild()) {
			cfgs = info.getManagedProject().getConfigurations();
		} else {
			cfgs = new IConfiguration[] {info.getDefaultConfiguration() };
		}

		for (IConfiguration cfg : cfgs) {
			updateOtherConfigs(cfg, kind);

			if(((Configuration)cfg).isInternalBuilderEnabled()){
				invokeInternalBuilder(cfg, kind != FULL_BUILD, ((Configuration)cfg).getInternalBuilderIgnoreErr(), monitor);

				// Scrub the build info the project
				info.setRebuildState(false);
				return referencedProjects;
			}

			// Create a makefile generator for the build
			IManagedBuilderMakefileGenerator generator = ManagedBuildManager.getBuildfileGenerator(info.getDefaultConfiguration());
			generator.initialize(getProject(), info, monitor);

			//perform necessary cleaning and build type calculation
			if(cfg.needsFullRebuild()){
				//configuration rebuild state is set to true,
				//full rebuild is needed in any case
				//clean first, then make a full build
				outputTrace(getProject().getName(), "config rebuild state is set to true, making a full rebuild");	//$NON-NLS-1$
				clean(new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN));
				fullBuild(info, generator, monitor);
			} else {
				boolean fullBuildNeeded = info.needsRebuild();
				IBuildDescription des = null;

				IResourceDelta delta = kind == FULL_BUILD ? null : getDelta(getProject());
				if(delta == null)
					fullBuildNeeded = true;
				if(cfg.needsRebuild() || delta != null){
					//use a build description model to calculate the resources to be cleaned
					//only in case there are some changes to the project sources or build information
					try{
						int flags = BuildDescriptionManager.REBUILD | BuildDescriptionManager.DEPFILES | BuildDescriptionManager.DEPS;
						if(delta != null)
							flags |= BuildDescriptionManager.REMOVED;

						outputTrace(getProject().getName(), "using a build description..");	//$NON-NLS-1$

						des = BuildDescriptionManager.createBuildDescription(info.getDefaultConfiguration(), getDelta(getProject()), flags);

						BuildDescriptionManager.cleanGeneratedRebuildResources(des);
					} catch (Throwable e){
						//TODO: log error
						outputError(getProject().getName(), "error occured while build description calculation: " + e.getLocalizedMessage());	//$NON-NLS-1$
						//in case an error occured, make it behave in the old stile:
						if(info.needsRebuild()){
							//make a full clean if an info needs a rebuild
							clean(new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN));
							fullBuildNeeded = true;
						}
						else if(delta != null && !fullBuildNeeded){
							// Create a delta visitor to detect the build type
							ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(info);
							delta.accept(visitor);
							if (visitor.shouldBuildFull()) {
								clean(new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN));
								fullBuildNeeded = true;
							}
						}
					}
				}

				if(fullBuildNeeded){
					outputTrace(getProject().getName(), "performing a full build");	//$NON-NLS-1$
					fullBuild(info, generator, monitor);
				} else {
					outputTrace(getProject().getName(), "performing an incremental build");	//$NON-NLS-1$
					incrementalBuild(delta, info, generator, monitor);
				}
			}
		}
		/*
		// So let's figure out why we got called
		if (kind == FULL_BUILD) {
			outputTrace(getProject().getName(), "Full build needed/requested");	//$NON-NLS-1$
			fullBuild(info, generator, monitor);
		}
		else if (kind == AUTO_BUILD && info.needsRebuild()) {
			outputTrace(getProject().getName(), "Autobuild requested, full build needed");	//$NON-NLS-1$
			fullBuild(info, generator, monitor);
		}
		else {
			// Create a delta visitor to make sure we should be rebuilding
			ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(info);
			IResourceDelta delta = getDelta(getProject());
			if (delta == null) {
				outputTrace(getProject().getName(), "Incremental build requested, full build needed");	//$NON-NLS-1$
				fullBuild(info, generator, monitor);
			}
			else {
				delta.accept(visitor);
				if (visitor.shouldBuildFull()) {
					outputTrace(getProject().getName(), "Incremental build requested, full build needed");	//$NON-NLS-1$
					fullBuild(info, generator, monitor);
				} else if (visitor.shouldBuildIncr()) {
					outputTrace(getProject().getName(), "Incremental build requested");	//$NON-NLS-1$
					incrementalBuild(delta, info, generator, monitor);
				}
				else if (referencedProjects != null) {
					//  Also check to see is any of the dependent projects changed
					for (int i=0; i<referencedProjects.length; i++) {
						IProject ref = referencedProjects[i];
						IResourceDelta refDelta = getDelta(ref);
						if (refDelta == null) {
							outputTrace(getProject().getName(), "Incremental build because of changed referenced project");	//$NON-NLS-1$
							incrementalBuild(delta, info, generator, monitor);
							//  Should only build this project once, for this delta
							break;
						} else {
							int refKind = refDelta.getKind();
							if (refKind != IResourceDelta.NO_CHANGE) {
								int refFlags = refDelta.getFlags();
								if (!(refKind == IResourceDelta.CHANGED &&
									  refFlags == IResourceDelta.OPEN)) {
									outputTrace(getProject().getName(), "Incremental build because of changed referenced project");	//$NON-NLS-1$
									incrementalBuild(delta, info, generator, monitor);
									//  Should only build this project once, for this delta
									break;
								}
							}
						}
					}
				}
			}
		}
		 */
		// Scrub the build info the project
		info.setRebuildState(false);
		// Ask build mechanism to compute deltas for project dependencies next time
		return referencedProjects;
	}

	private void updateOtherConfigs(IConfiguration cfg, int buildKind){
		new OtherConfigVerifier(cfg).updateOtherConfigs(buildKind == FULL_BUILD ? null : getDelta(getProject()));
	}

	/**
	 * Check whether the build has been canceled. Cancellation requests
	 * propagated to the caller by throwing <code>OperationCanceledException</code>.
	 *
	 * @see org.eclipse.core.runtime.OperationCanceledException#OperationCanceledException()
	 */
	public void checkCancel(IProgressMonitor monitor) {
		if (monitor != null && monitor.isCanceled()) {
			outputTrace(getProject().getName(), "Build cancelled");	//$NON-NLS-1$
			forgetLastBuiltState();
			throw new OperationCanceledException();
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.core.resources.IncrementalProjectBuilder#clean(org.eclipse.core.runtime.IProgressMonitor)
	 */
	@Override
	protected void clean(IProgressMonitor monitor) throws CoreException {
		if (DEBUG_EVENTS)
			printEvent(IncrementalProjectBuilder.CLEAN_BUILD, null);

		referencedProjects = getProject().getReferencedProjects();
		outputTrace(getProject().getName(), "Clean build requested");	//$NON-NLS-1$
		IManagedBuildInfo info = getBuildInfo();
		if (info == null) {
			outputError(getProject().getName(), "Build information was not found");	//$NON-NLS-1$
			return;
		}
		if (!info.isValid()) {
			outputError(getProject().getName(), "Build information is not valid");	//$NON-NLS-1$
			return;
		}
		IPath buildDirPath = getProject().getLocation().append(info.getConfigurationName());
		IWorkspace workspace = CCorePlugin.getWorkspace();
		IContainer buildDir = workspace.getRoot().getContainerForLocation(buildDirPath);
		if (buildDir == null || !buildDir.isAccessible()){
			outputError(buildDir == null ? "null" : buildDir.getName(), "Could not delete the build directory");	//$NON-NLS-1$ //$NON-NLS-2$
			return;
		}
		String status;
		try {
			// try the brute force approach first
			status = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.clean.deleting.output", buildDir.getName());	//$NON-NLS-1$
			monitor.subTask(status);
			workspace.delete(new IResource[]{buildDir}, true, monitor);
			StringBuffer buf = new StringBuffer();
			// write to the console
			IConsole console = CCorePlugin.getDefault().getConsole();
			console.start(getProject());
			ConsoleOutputStream consoleOutStream = console.getOutputStream();
			String[] consoleHeader = new String[3];
			consoleHeader[0] = ManagedMakeMessages.getResourceString(TYPE_CLEAN);
			consoleHeader[1] = info.getConfigurationName();
			consoleHeader[2] = getProject().getName();
			buf.append(NEWLINE);
			buf.append(ManagedMakeMessages.getFormattedString(CONSOLE_HEADER, consoleHeader)).append(NEWLINE);
			consoleOutStream.write(buf.toString().getBytes());
			consoleOutStream.flush();
			buf = new StringBuffer();
			// Report a successful clean
			String successMsg = ManagedMakeMessages.getFormattedString(BUILD_FINISHED, getProject().getName());
			buf.append(successMsg).append(NEWLINE);
			consoleOutStream.write(buf.toString().getBytes());
			consoleOutStream.flush();
			consoleOutStream.close();
		} catch (CoreException e) {
			// Create a makefile generator for the build
			status = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.clean.build.clean", buildDir.getName());	//$NON-NLS-1$
			monitor.subTask(status);
			IManagedBuilderMakefileGenerator generator = ManagedBuildManager.getBuildfileGenerator(info.getDefaultConfiguration());
			generator.initialize(getProject(), info, monitor);
			cleanBuild(info, generator, monitor);
		}  catch (IOException io) {}	//  Ignore console failures...
	}

	/* (non-Javadoc)
	 * @param info
	 * @param generator
	 * @param monitor
	 */
	protected void cleanBuild(IManagedBuildInfo info, IManagedBuilderMakefileGenerator generator, IProgressMonitor monitor) {
		// Make sure that there is a top level directory and a set of makefiles
		IPath buildDir = generator.getBuildWorkingDir();
		if (buildDir == null) {
			buildDir = new Path(info.getConfigurationName());
		}
		IPath makefilePath = getProject().getLocation().append(buildDir.append(generator.getMakefileName()));
		IWorkspaceRoot root = CCorePlugin.getWorkspace().getRoot();
		IFile makefile = root.getFileForLocation(makefilePath);

		if (makefile != null && makefile.isAccessible()) {
			// invoke make with the clean argument
			String statusMsg = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.starting", getProject().getName());	//$NON-NLS-1$
			monitor.subTask(statusMsg);
			checkCancel(monitor);
			invokeMake(CLEAN_BUILD, buildDir, info, generator, monitor);
		}
	}

	/* (non-Javadoc)
	 * @param info
	 * @param generator
	 * @param monitor
	 */
	protected void fullBuild(IManagedBuildInfo info, IManagedBuilderMakefileGenerator generator, IProgressMonitor monitor) throws CoreException {
		// Always need one of these bad boys
		if (monitor == null) {
			monitor = new NullProgressMonitor();
		}

		checkCancel(monitor);
		//If the previous builder invocation was cancelled, generated files might be corrupted
		//in case one or more of the generated makefiles (e.g. dep files) are corrupted,
		//the builder invocation might fail because of the possible syntax errors, so e.g. "make clean" will not work
		//we need to explicitly clean the generated directories
//		clean(new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN));

		// Regenerate the makefiles for this project
		checkCancel(monitor);
		String statusMsg = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.rebuild.makefiles", getProject().getName());	//$NON-NLS-1$
		monitor.subTask(statusMsg);
		//generator = ManagedBuildManager.getBuildfileGenerator(info.getDefaultConfiguration());
		generator.initialize(getProject(), info, monitor);
		MultiStatus result = generator.regenerateMakefiles();
		if (result.getCode() == IStatus.WARNING || result.getCode() == IStatus.INFO) {
			IStatus[] kids = result.getChildren();
			for (int index = 0; index < kids.length; ++index) {
				// One possibility is that there is nothing to build
				IStatus status = kids[index];
				if (status.getCode() == IManagedBuilderMakefileGenerator.NO_SOURCE_FOLDERS) {
					// Inform the user, via the console, that there is nothing to build
					// either because there are no buildable sources files or all potentially
					// buildable files have been excluded from build
					try {
						emitNoSourceMessage(FULL_BUILD, status, info.getConfigurationName());
					} catch (CoreException e) {
						// Throw the exception back to the builder
					    throw e;
					}
					// Dude, we're done
					return;
				} else {
					// Stick this in the list of stuff to warn the user about
					getGenerationProblems().add(status);
				}
			}
		}

		// Now call make
		checkCancel(monitor);
		statusMsg = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.starting", getProject().getName());	//$NON-NLS-1$
		monitor.subTask(statusMsg);
		IPath topBuildDir = generator.getBuildWorkingDir();
		if (topBuildDir != null) {
			invokeMake(FULL_BUILD, topBuildDir, info, generator, monitor);
		} else {
			statusMsg = ManagedMakeMessages.getFormattedString(NOTHING_BUILT, getProject().getName());
			monitor.subTask(statusMsg);
			return;
		}

		// Now regenerate the dependencies
		checkCancel(monitor);
		statusMsg = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.regen.deps", getProject().getName());	//$NON-NLS-1$
		monitor.subTask(statusMsg);
		try {
			generator.regenerateDependencies(false);
		} catch (CoreException e) {
			// Throw the exception back to the builder
			throw e;
		}

		//  Build finished message
		statusMsg = ManagedMakeMessages.getFormattedString(BUILD_FINISHED, getProject().getName());
		monitor.subTask(statusMsg);
	}

	/* (non-Javadoc)
	 *
	 * @return
	 */
	private Vector<IStatus> getGenerationProblems() {
		if (generationProblems == null) {
			generationProblems = new Vector<IStatus>();
		}
		return generationProblems;
	}

	/* (non-javadoc)
	 * Answers an array of strings with the proper make targets
        * for a build with no custom prebuild/postbuild steps
	 *
	 * @param fullBuild
	 * @return
	 */
	protected String[] getMakeTargets(int buildType) {
		List<String> args = new ArrayList<String>();
		switch (buildType) {
			case CLEAN_BUILD:
				args.add("clean");	//$NON-NLS-1$
				break;
			case FULL_BUILD:
			case INCREMENTAL_BUILD:
				args.add("all");	//$NON-NLS-1$
				break;
		}
		return args.toArray(new String[args.size()]);
	}

	protected List<IResource> getResourcesToBuild() {
		if (resourcesToBuild == null) {
			resourcesToBuild = new ArrayList<IResource>();
		}
		return resourcesToBuild;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.resources.ACBuilder#getWorkingDirectory()
	 */
	public IPath getWorkingDirectory() {
		return getProject().getLocation();
	}

	/* (non-Javadoc)
	 * @param delta
	 * @param info
	 * @param monitor
	 * @throws CoreException
	 */
	protected void incrementalBuild(IResourceDelta delta, IManagedBuildInfo info, IManagedBuilderMakefileGenerator generator, IProgressMonitor monitor) throws CoreException {
		// Need to report status to the user
		if (monitor == null) {
			monitor = new NullProgressMonitor();
		}

		// Ask the makefile generator to generate any makefiles needed to build delta
		checkCancel(monitor);
		String statusMsg = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.update.makefiles", getProject().getName());	//$NON-NLS-1$
		monitor.subTask(statusMsg);
		MultiStatus result = generator.generateMakefiles(delta);
		if (result.getCode() == IStatus.WARNING || result.getCode() == IStatus.INFO) {
			IStatus[] kids = result.getChildren();
			for (int index = 0; index < kids.length; ++index) {
				// One possibility is that there is nothing to build
				IStatus status = kids[index];
				if (status.getCode() == IManagedBuilderMakefileGenerator.NO_SOURCE_FOLDERS) {
					// Inform the user, via the console, that there is nothing to build
					// either because there are no buildable sources files or all potentially
					// buildable files have been excluded from build
					try {
						emitNoSourceMessage(INCREMENTAL_BUILD, status, info.getConfigurationName());
					} catch (CoreException e) {
						// Throw the exception back to the builder
					    throw e;
					}
					// Dude, we're done
					return;
				} else {
					// Stick this in the list of stuff to warn the user about
					getGenerationProblems().add(status);
				}
			}
		}

		// Run the build
		checkCancel(monitor);
		statusMsg = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.starting", getProject().getName());	//$NON-NLS-1$
		monitor.subTask(statusMsg);
		IPath buildDir = generator.getBuildWorkingDir();
		if (buildDir != null) {
			invokeMake(INCREMENTAL_BUILD, buildDir, info, generator, monitor);
		} else {
			statusMsg = ManagedMakeMessages.getFormattedString(NOTHING_BUILT, getProject().getName());
			monitor.subTask(statusMsg);
			return;
		}

		// Generate the dependencies for all changes
		checkCancel(monitor);
		statusMsg = ManagedMakeMessages.getFormattedString("ManagedMakeBuilder.message.updating.deps", getProject().getName());	//$NON-NLS-1$
		monitor.subTask(statusMsg);
		try {
			generator.generateDependencies();
		} catch (CoreException e) {
			throw e;
		}

		// Build finished message
		statusMsg = ManagedMakeMessages.getFormattedString(BUILD_FINISHED, getProject().getName());
		monitor.subTask(statusMsg);
}

	/* (non-Javadoc)
     * @param buildType
	 * @param buildDir
	 * @param info
     * @param generator
	 * @param monitor
	 */
	protected void invokeMake(int buildType, IPath buildDir, IManagedBuildInfo info, IManagedBuilderMakefileGenerator generator, IProgressMonitor monitor) {
		// Get the project and make sure there's a monitor to cancel the build
		IProject project = getProject();
		if (monitor == null) {
			monitor = new NullProgressMonitor();
		}

		try {
			// Figure out the working directory for the build and make sure there is a makefile there
			final URI workingDirectoryURI = getProject().getFolder(buildDir).getLocationURI();
			final String pathFromURI = EFSExtensionManager.getDefault().getPathFromURI(workingDirectoryURI);
			if(pathFromURI == null) {
				throw new CoreException(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, ManagedMakeMessages.getString("ManagedMakeBuilder.message.error"), null)); //$NON-NLS-1$
			}

			IPath workingDirectory = new Path(pathFromURI);

			IWorkspace workspace = project.getWorkspace();
			if (workspace == null) {
				return;
			}
			IWorkspaceRoot root = workspace.getRoot();
			if (root == null) {
				return;
			}
			IPath makefile = workingDirectory.append(generator.getMakefileName());
			if (root.getFileForLocation(makefile) == null) {
				return;
			}

			// Flag to the user that make is about to be called
			String makeCmd = info.getBuildCommand();
			//try to resolve the build macros in the builder command
			try{
				String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
						makeCmd,
						"", //$NON-NLS-1$
						" ", //$NON-NLS-1$
						IBuildMacroProvider.CONTEXT_CONFIGURATION,
						info.getDefaultConfiguration());
				if((resolved = resolved.trim()).length() > 0)
					makeCmd = resolved;
			} catch (BuildMacroException e){
			}

			IPath makeCommand = new Path(makeCmd);
			String[] msgs = new String[2];
			msgs[0] = makeCommand.toString();
			msgs[1] = project.getName();
			monitor.subTask(ManagedMakeMessages.getFormattedString(MAKE, msgs));

			// Get a build console for the project
			StringBuffer buf = new StringBuffer();
			IConsole console = CCorePlugin.getDefault().getConsole();
			console.start(project);
			ConsoleOutputStream consoleOutStream = console.getOutputStream();
			String[] consoleHeader = new String[3];
			switch (buildType) {
				case FULL_BUILD:
				case INCREMENTAL_BUILD:
					consoleHeader[0] = ManagedMakeMessages.getResourceString(TYPE_INC);
					break;
				case CLEAN_BUILD:
					consoleHeader[0] = ManagedMakeMessages.getResourceString(TYPE_CLEAN);
					break;
			}

			consoleHeader[1] = info.getConfigurationName();
			consoleHeader[2] = project.getName();
			buf.append(NEWLINE);
			buf.append(ManagedMakeMessages.getFormattedString(CONSOLE_HEADER, consoleHeader)).append(NEWLINE);
			buf.append(NEWLINE);

			IConfiguration cfg = info.getDefaultConfiguration();
			if(!cfg.isSupported()){
				String msg = ManagedMakeMessages.getFormattedString(WARNING_UNSUPPORTED_CONFIGURATION,new String[] {cfg.getName(),cfg.getToolChain().getName()});
				buf.append(msg).append(NEWLINE);
				buf.append(NEWLINE);
			}
			consoleOutStream.write(buf.toString().getBytes());
			consoleOutStream.flush();

			// Remove all markers for this project
			removeAllMarkers(project);

			// Get a launcher for the make command
			String errMsg = null;
			IBuilder builder = info.getDefaultConfiguration().getBuilder();
			ICommandLauncher launcher = builder.getCommandLauncher();
			launcher.setProject(project);
			launcher.showCommand(true);

			// Set the environmennt
			IBuildEnvironmentVariable variables[] = ManagedBuildManager.getEnvironmentVariableProvider().getVariables(cfg,true,true);
			String[] envp = null;
			ArrayList<String> envList = new ArrayList<String>();
			if (variables != null) {
				for(int i = 0; i < variables.length; i++){
					envList.add(variables[i].getName() + "=" + variables[i].getValue());	//$NON-NLS-1$
				}
				envp = envList.toArray(new String[envList.size()]);
			}

			// Hook up an error parser manager
			String[] errorParsers = info.getDefaultConfiguration().getErrorParserList();
			ErrorParserManager epm = new ErrorParserManager(getProject(), workingDirectoryURI, this, errorParsers);
			epm.setOutputStream(consoleOutStream);
			// This variable is necessary to ensure that the EPM stream stay open
			// until we explicitly close it. See bug#123302.
			OutputStream epmOutputStream = epm.getOutputStream();

			// Get the arguments to be passed to make from build model
			ArrayList<String> makeArgs = new ArrayList<String>();
			String arg = info.getBuildArguments();
			if (arg.length() > 0) {
				String[] args = arg.split("\\s"); //$NON-NLS-1$
				for (int i = 0; i < args.length; ++i) {
					makeArgs.add(args[i]);
				}
			}

			String[] makeTargets;
			String prebuildStep = info.getPrebuildStep();
			//try to resolve the build macros in the prebuildStep
			try{
				prebuildStep = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat(
						prebuildStep,
						"", //$NON-NLS-1$
						" ", //$NON-NLS-1$
						IBuildMacroProvider.CONTEXT_CONFIGURATION,
						cfg);
			} catch (BuildMacroException e){
			}
			boolean prebuildStepPresent = (prebuildStep.length() > 0);
			Process proc = null;
			boolean isuptodate = false;

			if (prebuildStepPresent) {
				@SuppressWarnings("unchecked")
				ArrayList<String> premakeArgs = (ArrayList<String>) makeArgs.clone();
				String[] premakeTargets;
				switch (buildType) {
				case INCREMENTAL_BUILD: {
					// For an incremental build with a prebuild step:
					// Check the status of the main build with "make -q main-build"
					// If up to date:
					// then: don't invoke the prebuild step, which should be run only if
					//       something needs to be built in the main build
					// else: invoke the prebuild step and the main build step
					premakeArgs.add("-q"); //$NON-NLS-1$
					premakeArgs.add("main-build"); //$NON-NLS-1$
					premakeTargets = premakeArgs.toArray(new String[premakeArgs.size()]);
					proc = launcher.execute(makeCommand, premakeTargets, envp, workingDirectory, monitor);
					if (proc != null) {
						try {
							// Close the input of the process since we will never write to it
							proc.getOutputStream().close();
						} catch (IOException e) {
						}
						if (launcher.waitAndRead(epm.getOutputStream(), epm.getOutputStream(),
								new SubProgressMonitor(monitor,
										IProgressMonitor.UNKNOWN)) != ICommandLauncher.OK) {
							errMsg = launcher.getErrorMessage();
						}
					} else {
						errMsg = launcher.getErrorMessage();
					}

					if ((errMsg != null && errMsg.length() > 0) || proc == null) {
						// Can't tell if the build is needed, so assume it is, and let any errors be triggered
						// when the "real" build is invoked below
						makeArgs.add("pre-build"); //$NON-NLS-1$
						makeArgs.add("main-build"); //$NON-NLS-1$
					} else {
						// The "make -q" command launch was successful
						if (proc.exitValue() == 0) {
							// If the status value returned from "make -q" is 0, then the build state is up-to-date
							isuptodate = true;
							// Report that the build was up to date, and thus nothing needs to be built
							String uptodateMsg = ManagedMakeMessages.getFormattedString(NOTHING_BUILT, project.getName());
							buf = new StringBuffer();
							buf.append(NEWLINE);
							buf.append(uptodateMsg).append(NEWLINE);
							// Write message on the console
							consoleOutStream.write(buf.toString().getBytes());
							consoleOutStream.flush();
							epmOutputStream.close();
							consoleOutStream.close();
						} else {
							// The status value was other than 0, so press on with the build process
							makeArgs.add("pre-build"); //$NON-NLS-1$
							makeArgs.add("main-build"); //$NON-NLS-1$
						}
					}
					break;
				}
				case FULL_BUILD: {
//						makeArgs.add("clean"); //$NON-NLS-1$
					makeArgs.add("pre-build"); //$NON-NLS-1$
					makeArgs.add("main-build"); //$NON-NLS-1$
					break;
				}
				case CLEAN_BUILD: {
					makeArgs.add("clean"); //$NON-NLS-1$
					break;
				}
				}

			} else {
				// No prebuild step
				//
				makeArgs.addAll(Arrays.asList(getMakeTargets(buildType)));
			}

			makeTargets = makeArgs.toArray(new String[makeArgs.size()]);

			// Launch make - main invocation
			if (!isuptodate) {
				proc = launcher.execute(makeCommand, makeTargets, envp, workingDirectory, monitor);
				if (proc != null) {
					try {
						// Close the input of the process since we will never write to it
						proc.getOutputStream().close();
					} catch (IOException e) {
					}

					int state = launcher.waitAndRead(epm.getOutputStream(), epm.getOutputStream(),
							new SubProgressMonitor(monitor,
									IProgressMonitor.UNKNOWN));
					if(state != ICommandLauncher.OK){
						errMsg = launcher.getErrorMessage();

						if(state == ICommandLauncher.COMMAND_CANCELED){
							//TODO: the better way of handling cancel is needed
							//currently the rebuild state is set to true forcing the full rebuild
							//on the next builder invocation
							info.getDefaultConfiguration().setRebuildState(true);
						}
					}

					// Force a resync of the projects without allowing the user to cancel.
					// This is probably unkind, but short of this there is no way to insure
					// the UI is up-to-date with the build results
					monitor.subTask(ManagedMakeMessages
							.getResourceString(REFRESH));
					try {
						//currentProject.refreshLocal(IResource.DEPTH_INFINITE, null);

						// use the refresh scope manager to refresh
						RefreshScopeManager refreshManager = RefreshScopeManager.getInstance();
						IWorkspaceRunnable runnable = refreshManager.getRefreshRunnable(project, cfg.getName());
						ResourcesPlugin.getWorkspace().run(runnable, null, IWorkspace.AVOID_UPDATE, null);
					} catch (CoreException e) {
						monitor.subTask(ManagedMakeMessages
								.getResourceString(REFRESH_ERROR));
					}
				} else {
					errMsg = launcher.getErrorMessage();
				}

				// Report either the success or failure of our mission
				buf = new StringBuffer();
				if (errMsg != null && errMsg.length() > 0) {
					buf.append(errMsg).append(NEWLINE);
				} else {
					// Report a successful build
					String successMsg = ManagedMakeMessages.getFormattedString(BUILD_FINISHED,
							project.getName());
					buf.append(successMsg).append(NEWLINE);
				}

				// Write message on the console
				consoleOutStream.write(buf.toString().getBytes());
				consoleOutStream.flush();
				epmOutputStream.close();

				// Generate any error markers that the build has discovered
				monitor.subTask(ManagedMakeMessages.getResourceString(MARKERS));
				addBuilderMarkers(epm);
				consoleOutStream.close();
			}
		} catch (Exception e) {
			forgetLastBuiltState();
		} finally {
			getGenerationProblems().clear();
		}
	}

	/* (non-Javadoc)
	 * Removes the IMarkers for the project specified in the argument if the
	 * project exists, and is open.
	 *
	 * @param project
	 */
	private void removeAllMarkers(IProject project) {
		if (project == null || !project.isAccessible()) return;

		// Clear out the problem markers
		IWorkspace workspace = project.getWorkspace();
		IMarker[] markers;
		try {
			markers = project.findMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
		} catch (CoreException e) {
			// Handled just about every case in the sanity check
			return;
		}
		if (markers != null) {
			try {
				workspace.deleteMarkers(markers);
			} catch (CoreException e) {
				// The only situation that might cause this is some sort of resource change event
				return;
			}
		}
	}

	/**
	 * called to invoke the MBS Internal Builder for building the given configuration
	 *
	 * @param cfg configuration to be built
	 * @param buildIncrementaly if true, incremental build will be performed,
	 * only files that need rebuild will be built.
	 * If false, full rebuild will be performed
	 * @param resumeOnErr if true, build will continue in case of error while building.
	 * If false the build will stop on the first error
	 * @param monitor monitor
	 */
	protected void invokeInternalBuilder(IConfiguration cfg,
			boolean buildIncrementaly,
			boolean resumeOnErr,
			IProgressMonitor monitor) {

		boolean isParallel = ((Configuration)cfg).getParallelDef();

		// Get the project and make sure there's a monitor to cancel the build
		IProject currentProject = cfg.getOwner().getProject();
		if (monitor == null) {
			monitor = new NullProgressMonitor();
		}

		String[] msgs = new String[2];
		msgs[0] = ManagedMakeMessages.getResourceString(INTERNAL_BUILDER);
		msgs[1] = currentProject.getName();

		ConsoleOutputStream consoleOutStream = null;
		IConsole console = null;
		OutputStream epmOutputStream = null;
		try {
			int flags = 0;
			IResourceDelta delta = null;

			if(buildIncrementaly){
				flags = BuildDescriptionManager.REBUILD | BuildDescriptionManager.REMOVED | BuildDescriptionManager.DEPS;
				delta = getDelta(currentProject);
			}

			IBuildDescription des = BuildDescriptionManager.createBuildDescription(cfg, delta, flags);

			DescriptionBuilder builder = null;
			if (!isParallel)
				builder = new DescriptionBuilder(des, buildIncrementaly, resumeOnErr, null);

			// Get a build console for the project
			StringBuffer buf = new StringBuffer();
			console = CCorePlugin.getDefault().getConsole();
			console.start(currentProject);
			consoleOutStream = console.getOutputStream();
			String[] consoleHeader = new String[3];
			if(buildIncrementaly)
				consoleHeader[0] = ManagedMakeMessages.getResourceString(TYPE_INC);
			else
				consoleHeader[0] = ManagedMakeMessages.getResourceString(TYPE_REBUILD);

			consoleHeader[1] = cfg.getName();
			consoleHeader[2] = currentProject.getName();
			buf.append(NEWLINE);
			buf.append(ManagedMakeMessages.getFormattedString(CONSOLE_HEADER, consoleHeader)).append(NEWLINE);
			buf.append(NEWLINE);

			buf.append(ManagedMakeMessages.getResourceString(INTERNAL_BUILDER_HEADER_NOTE));
			buf.append("\n"); //$NON-NLS-1$

			if(!cfg.isSupported()){
				String msg = ManagedMakeMessages.getFormattedString(WARNING_UNSUPPORTED_CONFIGURATION,new String[] {cfg.getName(),cfg.getToolChain().getName()});
				buf.append(msg).append(NEWLINE);
				buf.append(NEWLINE);
			}
			consoleOutStream.write(buf.toString().getBytes());
			consoleOutStream.flush();

			if(isParallel || builder.getNumCommands() > 0) {
				// Remove all markers for this project
				removeAllMarkers(currentProject);

				// Hook up an error parser manager
				String[] errorParsers = cfg.getErrorParserList();
				ErrorParserManager epm = new ErrorParserManager(getProject(), des.getDefaultBuildDirLocationURI(), this, errorParsers);
				epm.setOutputStream(consoleOutStream);
				// This variable is necessary to ensure that the EPM stream stay open
				// until we explicitly close it. See bug#123302.
				epmOutputStream = epm.getOutputStream();

				int status = 0;

				long t1 = System.currentTimeMillis();
				if (isParallel)
					status = ParallelBuilder.build(des, null, null, epmOutputStream, epmOutputStream, monitor, resumeOnErr, buildIncrementaly);
				else
					status = builder.build(epmOutputStream, epmOutputStream, monitor);
				long t2 = System.currentTimeMillis();

				// Report either the success or failure of our mission
				buf = new StringBuffer();

				switch(status){
				case IBuildModelBuilder.STATUS_OK:
					buf.append(ManagedMakeMessages
							.getFormattedString(BUILD_FINISHED,
									currentProject.getName()));
					break;
				case IBuildModelBuilder.STATUS_CANCELLED:
					buf.append(ManagedMakeMessages
							.getResourceString(BUILD_CANCELLED));
					break;
				case IBuildModelBuilder.STATUS_ERROR_BUILD:
					String msg = resumeOnErr ?
							ManagedMakeMessages.getResourceString(BUILD_FINISHED_WITH_ERRS) :
								ManagedMakeMessages.getResourceString(BUILD_STOPPED_ERR);
					buf.append(msg);
					break;
				case IBuildModelBuilder.STATUS_ERROR_LAUNCH:
				default:
					buf.append(ManagedMakeMessages
							.getResourceString(BUILD_FAILED_ERR));
					break;
				}
				buf.append(NEWLINE);

				// Report time and number of threads used
//				buf.append("Time consumed: ");
//				buf.append(t2 - t1);
//				buf.append(" ms.  ");
//				if (isParallel) {
//					buf.append("Parallel threads used: ");
//					buf.append(ParallelBuilder.lastThreadsUsed);
//				}
//				buf.append("\n");

				// Report time and number of threads used
				buf.append(ManagedMakeMessages.getFormattedString("CommonBuilder.6", Integer.toString((int)(t2 - t1)))); //$NON-NLS-1$
//				buf.append(t2 - t1);
//				buf.append(" ms.  ");
				if (isParallel) {
					buf.append(ManagedMakeMessages.getFormattedString("CommonBuilder.7", Integer.toString(ParallelBuilder.lastThreadsUsed))); //$NON-NLS-1$
//					buf.append(ParallelBuilder.lastThreadsUsed);
				}
				buf.append(NEWLINE);

				// Write message on the console
				consoleOutStream.write(buf.toString().getBytes());
				consoleOutStream.flush();
				epmOutputStream.close();
				epmOutputStream = null;
				// Generate any error markers that the build has discovered
				monitor.subTask(ManagedMakeMessages.getResourceString(MARKERS));
				addBuilderMarkers(epm);
			} else {
				buf = new StringBuffer();
				buf.append(ManagedMakeMessages.getFormattedString(NOTHING_BUILT, getProject().getName())).append(NEWLINE);
				consoleOutStream.write(buf.toString().getBytes());
				consoleOutStream.flush();
			}

		} catch (Exception e) {
			if(consoleOutStream != null){
				StringBuffer buf = new StringBuffer();
				String errorDesc = ManagedMakeMessages.getResourceString(BUILD_ERROR);
				buf.append(errorDesc).append(NEWLINE);
				buf.append("(").append(e.getLocalizedMessage()).append(")").append(NEWLINE); //$NON-NLS-1$ //$NON-NLS-2$

				try {
					consoleOutStream.write(buf.toString().getBytes());
					consoleOutStream.flush();
				} catch (IOException e1) {
				}
			}
			forgetLastBuiltState();
		} finally {
			if(epmOutputStream != null){
				try {
					epmOutputStream.close();
				} catch (IOException e) {
				}
			}
			if(consoleOutStream != null){
				try {
					consoleOutStream.close();
				} catch (IOException e) {
				}
			}
			getGenerationProblems().clear();
			monitor.done();
		}
	}

	private Map<IProject, List<IFile>> arrangeFilesByProject(List<IFile> files) {
		Map<IProject, List<IFile>> projectMap = new HashMap<IProject, List<IFile>>();
		for (IFile file : files) {
			IProject project = file.getProject();
			List<IFile> filesInProject = projectMap.get(project);
			if (filesInProject == null) {
				filesInProject = new ArrayList<IFile>();
				projectMap.put(project, filesInProject);
			}
			filesInProject.add(file);
		}
		return projectMap;
	}

	/**
	 * Called to invoke the MBS Internal Builder for building the given resources
	 *
	 * @param files - list of files to build.
	 * @param monitor - progress monitor to report progress to user.
	 * @return status of the operation. Can be {@link Status#OK_STATUS} or
	 *    {@link Status#CANCEL_STATUS}.
	 */
	public IStatus invokeInternalBuilder(List<IFile> files, IProgressMonitor monitor) {
		// Make sure there's a monitor to cancel the build
		if (monitor == null) {
			monitor = new NullProgressMonitor();
		}

		try {
			Map<IProject, List<IFile>> projectMap = arrangeFilesByProject(files);
			monitor.beginTask("", projectMap.size() * PROGRESS_MONITOR_SCALE); //$NON-NLS-1$

			for (List<IFile> filesInProject : projectMap.values()) {
				IProject project = filesInProject.get(0).getProject();
				monitor.subTask(ManagedMakeMessages.getFormattedString("GeneratedMakefileBuilder.buildingProject", project.getName())); //$NON-NLS-1$
				invokeInternalBuilderForOneProject(filesInProject, new SubProgressMonitor(monitor, 1 * PROGRESS_MONITOR_SCALE, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK));
			}
		} finally {
			if (monitor.isCanceled()) {
				return Status.CANCEL_STATUS;
			}
			monitor.done();
		}
		return Status.OK_STATUS;
	}

	private void invokeInternalBuilderForOneProject(List<IFile> files, IProgressMonitor monitor) {
		IProject project = files.get(0).getProject();
		BuildRunnerHelper buildRunnerHelper = new BuildRunnerHelper(project);

		try {
			monitor.beginTask(ManagedMakeMessages.getFormattedString("GeneratedMakefileBuilder.buildingProject", project.getName()) + ':', //$NON-NLS-1$
					TICKS_STREAM_PROGRESS_MONITOR + files.size() * PROGRESS_MONITOR_SCALE);

			// Get a build console for the project
			console = CCorePlugin.getDefault().getConsole();
			console.start(project);

			IManagedBuildInfo buildInfo = ManagedBuildManager.getBuildInfo(project);
			IConfiguration configuration = buildInfo.getDefaultConfiguration();

			String cfgName = configuration.getName();
			String toolchainName = configuration.getToolChain().getName();
			boolean isSupported = configuration.isSupported();

			IBuildDescription des = BuildDescriptionManager.createBuildDescription(configuration, null, 0);

			String[] errorParsers = configuration.getErrorParserList();
			ErrorParserManager epm = new ErrorParserManager(project, des.getDefaultBuildDirLocationURI(), this, errorParsers);

			buildRunnerHelper.prepareStreams(epm, null, console, new SubProgressMonitor(monitor, TICKS_STREAM_PROGRESS_MONITOR));
			OutputStream stdout = buildRunnerHelper.getOutputStream();
			OutputStream stderr = buildRunnerHelper.getErrorStream();

			buildRunnerHelper.greeting(ManagedMakeMessages.getResourceString("BuildFilesAction.buildingSelectedFiles"), cfgName, toolchainName, isSupported); //$NON-NLS-1$
			buildRunnerHelper.printLine(ManagedMakeMessages.getResourceString("ManagedMakeBuilder.message.internal.builder.header.note")); //$NON-NLS-1$

			// Build artifacts for each file in the project
			for (IFile file : files) {
				if (monitor.isCanceled()) {
					break;
				}
				String filePath = file.getProjectRelativePath().toString();

				try {
					IBuildResource buildResource = des.getBuildResource(file);

					Set<IBuildStep> dependentSteps = new HashSet<IBuildStep>();
					IBuildIOType depTypes[] = buildResource.getDependentIOTypes();
					for (IBuildIOType btype : depTypes) {
						if(btype != null && btype.getStep() != null)
							dependentSteps.add(btype.getStep());
					}

					SubProgressMonitor monitor2 = new SubProgressMonitor(monitor, 1 * PROGRESS_MONITOR_SCALE, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
					try {
						monitor2.beginTask("", TICKS_DELETE_MARKERS + dependentSteps.size()*PROGRESS_MONITOR_SCALE); //$NON-NLS-1$

						// Remove problem markers for the file
						monitor2.subTask(ManagedMakeMessages.getFormattedString("GeneratedMakefileBuilder.removingResourceMarkers", filePath)); //$NON-NLS-1$
						buildRunnerHelper.removeOldMarkers(file,  new SubProgressMonitor(monitor2, TICKS_DELETE_MARKERS, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));

						// Build dependent steps
						for (IBuildStep step : dependentSteps) {
							if (monitor2.isCanceled()) {
								break;
							}

							monitor2.subTask(filePath);
							StepBuilder stepBuilder = new StepBuilder(step, null);
							stepBuilder.build(stdout, stderr, new SubProgressMonitor(monitor2, 1 * PROGRESS_MONITOR_SCALE, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));

							monitor2.subTask(ManagedMakeMessages.getFormattedString("GeneratedMakefileBuilder.refreshingArtifacts", filePath)); //$NON-NLS-1$
							IBuildIOType[] outputIOTypes = step.getOutputIOTypes();
							for (IBuildIOType type : outputIOTypes) {
								for (IBuildResource outResource : type.getResources()) {
									IFile outFile = project.getFile(outResource.getLocation());
									// Refresh the output resource without allowing the user to cancel.
									outFile.refreshLocal(IResource.DEPTH_INFINITE, null);
								}
							}
						}
					} finally {
						monitor2.done();
					}

				} catch (Exception e) {
					ManagedBuilderCorePlugin.log(new CoreException(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, "CDT Build Error", e))); //$NON-NLS-1$
				}

			}
			buildRunnerHelper.close();
			buildRunnerHelper.goodbye();

		} catch (Exception e) {
			ManagedBuilderCorePlugin.log(new CoreException(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, "CDT Build Error", e))); //$NON-NLS-1$
			forgetLastBuiltState();
		} finally {
			getGenerationProblems().clear();
			try {
				buildRunnerHelper.close();
			} catch (IOException e) {
				ManagedBuilderCorePlugin.log(new CoreException(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, "CDT Build Error", e))); //$NON-NLS-1$
			}
			monitor.done();
		}
	}

	public IStatus cleanFiles(List<IFile> files, IProgressMonitor monitor) {
		// Make sure there's a monitor to cancel the build
		if (monitor == null) {
			monitor = new NullProgressMonitor();
		}

		try {
			Map<IProject, List<IFile>> projectMap = arrangeFilesByProject(files);
			monitor.beginTask("", projectMap.size() * PROGRESS_MONITOR_SCALE); //$NON-NLS-1$

			for (List<IFile> filesInProject : projectMap.values()) {
				IProject project = filesInProject.get(0).getProject();
				monitor.subTask(ManagedMakeMessages.getFormattedString("GeneratedMakefileBuilder.cleaningProject", project.getName())); //$NON-NLS-1$
				cleanFilesForOneProject(filesInProject, new SubProgressMonitor(monitor, 1 * PROGRESS_MONITOR_SCALE, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK));
			}
		} finally {
			if (monitor.isCanceled()) {
				return Status.CANCEL_STATUS;
			}
			monitor.done();
		}
		return Status.OK_STATUS;
	}

	public void cleanFilesForOneProject(List<IFile> files, IProgressMonitor monitor) {
		IProject project = files.get(0).getProject();
		BuildRunnerHelper buildRunnerHelper = new BuildRunnerHelper(project);
		int countDeleted = 0;

		try {
			monitor.beginTask(ManagedMakeMessages.getFormattedString("GeneratedMakefileBuilder.cleaningProject", project.getName()) + ':', //$NON-NLS-1$
					TICKS_STREAM_PROGRESS_MONITOR + files.size() * PROGRESS_MONITOR_SCALE);

			// Get a build console for the project
			console = CCorePlugin.getDefault().getConsole();
			console.start(project);

			IManagedBuildInfo buildInfo = ManagedBuildManager.getBuildInfo(project);
			IConfiguration configuration = buildInfo.getDefaultConfiguration();

			String cfgName = configuration.getName();
			String toolchainName = configuration.getToolChain().getName();
			boolean isSupported = configuration.isSupported();

			int flags = BuildDescriptionManager.REBUILD | BuildDescriptionManager.REMOVED;
			IResourceDelta delta = getDelta(project);

			IBuildDescription des = BuildDescriptionManager.createBuildDescription(configuration, delta, flags);

			String[] errorParsers = configuration.getErrorParserList();
			ErrorParserManager epm = new ErrorParserManager(project, des.getDefaultBuildDirLocationURI(), this, errorParsers);
			buildRunnerHelper.prepareStreams(epm, null , console, new SubProgressMonitor(monitor, TICKS_STREAM_PROGRESS_MONITOR));

			buildRunnerHelper.greeting(ManagedMakeMessages.getResourceString("CleanFilesAction.cleanSelectedFiles"), cfgName, toolchainName, isSupported); //$NON-NLS-1$
			buildRunnerHelper.printLine(ManagedMakeMessages.getResourceString("ManagedMakeBuilder.message.internal.builder.header.note")); //$NON-NLS-1$

			for (IFile file : files) {
				if (monitor.isCanceled()) {
					break;
				}
				String filePath = file.getProjectRelativePath().toString();

				try {
					IBuildResource buildResource = des.getBuildResource(file);
					if (buildResource != null) {
						Set<IBuildStep> dependentSteps = new HashSet<IBuildStep>();
						IBuildIOType depTypes[] = buildResource.getDependentIOTypes();
						for (IBuildIOType btype : depTypes) {
							if (btype != null && btype.getStep() != null)
								dependentSteps.add(btype.getStep());
						}

						SubProgressMonitor monitor2 = new SubProgressMonitor(monitor, 1 * PROGRESS_MONITOR_SCALE, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
						try {
							monitor2.beginTask("", TICKS_DELETE_MARKERS + dependentSteps.size()*PROGRESS_MONITOR_SCALE); //$NON-NLS-1$

							// Remove problem markers for the file
							monitor2.subTask(ManagedMakeMessages.getFormattedString("GeneratedMakefileBuilder.removingResourceMarkers", filePath)); //$NON-NLS-1$
							buildRunnerHelper.removeOldMarkers(file,  new SubProgressMonitor(monitor2, TICKS_DELETE_MARKERS, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));

							// iterate through all build steps
							for (IBuildStep step : dependentSteps) {
								if (monitor2.isCanceled()) {
									break;
								}

								monitor2.subTask(filePath);
								// Delete the output resources
								IBuildIOType[] outputIOTypes = step.getOutputIOTypes();

								for (IBuildIOType ioType : outputIOTypes) {
									for (IBuildResource rc : ioType.getResources()) {
										IResource outputFile = project.findMember(rc.getFullPath().removeFirstSegments(1)); // strip project name
										if (outputFile != null) {
											outputFile.delete(true, null);
											countDeleted++;
											buildRunnerHelper.printLine(ManagedMakeMessages.getFormattedString("GeneratedMakefileBuilder.fileDeleted", //$NON-NLS-1$
													outputFile.getProjectRelativePath().toString()));
										}
									}
								}

								monitor2.worked(1 * PROGRESS_MONITOR_SCALE);
							}
						} finally {
							monitor2.done();
						}
					}
				} catch (Exception e) {
					ManagedBuilderCorePlugin.log(new CoreException(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, "CDT Build Error", e))); //$NON-NLS-1$
				}

			}
			if (countDeleted == 0) {
				buildRunnerHelper.printLine(ManagedMakeMessages.getResourceString("GeneratedMakefileBuilder.nothingToClean")); //$NON-NLS-1$
			}
			buildRunnerHelper.close();
			buildRunnerHelper.goodbye();

		} catch (Exception e) {
			ManagedBuilderCorePlugin.log(new CoreException(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, "CDT Build Error", e))); //$NON-NLS-1$
		} finally {
			try {
				buildRunnerHelper.close();
			} catch (IOException e) {
				ManagedBuilderCorePlugin.log(new CoreException(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, "CDT Build Error", e))); //$NON-NLS-1$
			}
			monitor.done();
		}
	}
}
